#ifndef COUNTED_PTR_H_INCLUDED
#define COUNTED_PTR_H_INCLUDED

/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 counted_ptr.h

	$Header: /resource_editor/counted_ptr.h $

	$NoKeywords: $

 ************************************************************************/

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

// -------------------------------------------------------------------------------------------
// helper function for t_counted_ptr class template to ensure that one pointer type can be
// implicitly converted to another
// -------------------------------------------------------------------------------------------
template <class type>
inline type implicit_cast( type obj )
{
	return obj;
}

// -------------------------------------------------------------------------------------------
// base class for all reference counted objects.  Such objects must never appear on the stack.
// -------------------------------------------------------------------------------------------
class t_counted_object
{
public:
	t_counted_object();
	t_counted_object( const t_counted_object& arg );
	virtual ~t_counted_object();

	t_counted_object& operator=( t_counted_object const& arg );
	int get_reference_count() const;
private:
	friend class t_counted_ptr_base;

	void add_reference() const;
	void remove_reference() const;
	mutable int m_reference_count;
};

// -------------------------------------------------------------------------------------------
// inlines for base reference counted object
// -------------------------------------------------------------------------------------------
inline t_counted_object::t_counted_object()
{
	m_reference_count = 0;
}

inline t_counted_object::t_counted_object( t_counted_object const& )
{
	m_reference_count = 0;
}

inline t_counted_object::~t_counted_object()
{
}

inline t_counted_object& t_counted_object::operator =( t_counted_object const& arg )
{
	// don't copy the reference count, keep our own.
	return *this;
}

inline void t_counted_object::add_reference() const
{
	if (this)
		m_reference_count++;
}

inline int t_counted_object::get_reference_count() const
{
	return m_reference_count;
}

inline void t_counted_object::remove_reference() const
{
	if (this && --m_reference_count == 0)
		delete this;
}


// -------------------------------------------------------------------------------------------
// abstract reference counted pointer.
// -------------------------------------------------------------------------------------------
class t_counted_ptr_base
{
public:
	t_counted_ptr_base();
	t_counted_ptr_base( t_counted_object const * object );
	t_counted_ptr_base( t_counted_ptr_base const& pointer );
	~t_counted_ptr_base();

	bool operator==( t_counted_object const* arg ) const;
	bool operator!=( t_counted_object const* arg ) const;
	bool operator==( t_counted_ptr_base const& arg ) const;
	bool operator!=( t_counted_ptr_base const& arg ) const;
	bool operator!() const;

	bool operator<(t_counted_ptr_base const& arg ) const; 
	bool operator<=(t_counted_ptr_base const& arg ) const; 
	bool operator>(t_counted_ptr_base const& arg ) const; 
	bool operator>=(t_counted_ptr_base const& arg ) const; 

	t_counted_ptr_base& operator=( t_counted_object const * object );
	t_counted_ptr_base& operator=( t_counted_ptr_base const& arg );
protected:
	t_counted_object const * m_object;
};

// -------------------------------------------------------------------------------------------
// inlines for abstract reference counted pointer.
// -------------------------------------------------------------------------------------------
inline t_counted_ptr_base::t_counted_ptr_base()
{
	m_object = 0;
}

inline t_counted_ptr_base::t_counted_ptr_base( t_counted_object const * object )
{
	m_object = object;
	object->add_reference();
}

inline t_counted_ptr_base::t_counted_ptr_base( t_counted_ptr_base const& pointer )
{
	m_object = pointer.m_object;
	m_object->add_reference();
}

inline t_counted_ptr_base::~t_counted_ptr_base()
{
	m_object->remove_reference();
}


inline bool t_counted_ptr_base::operator==( t_counted_object const* arg ) const
{ 
	return m_object == arg; 
}

inline bool t_counted_ptr_base::operator!=( t_counted_object const* arg ) const
{
	return m_object != arg; 
}

inline bool t_counted_ptr_base::operator==( t_counted_ptr_base const& arg ) const
{
	return m_object == arg.m_object;
}

inline bool t_counted_ptr_base::operator!=( t_counted_ptr_base const& arg ) const
{
	return m_object != arg.m_object;
}

inline bool t_counted_ptr_base::operator!() const
{
	return m_object == 0;
}

inline t_counted_ptr_base& t_counted_ptr_base::operator=( t_counted_object const * object )
{
	object->add_reference();
	m_object->remove_reference();
	m_object = object;
	return *this;
}

inline t_counted_ptr_base& t_counted_ptr_base::operator=( t_counted_ptr_base const& arg )
{
	t_counted_object* object = const_cast<t_counted_object*>(arg.m_object);

	object->add_reference();
	m_object->remove_reference();
	m_object = object;
	return *this;
}

inline
bool t_counted_ptr_base::operator<(t_counted_ptr_base const& arg ) const
{
	return m_object < arg.m_object;
}

inline
bool t_counted_ptr_base::operator<=(t_counted_ptr_base const& arg ) const
{
	return m_object <= arg.m_object;
}

inline
bool t_counted_ptr_base::operator>(t_counted_ptr_base const& arg ) const
{
	return m_object > arg.m_object;
}

inline
bool t_counted_ptr_base::operator>=(t_counted_ptr_base const& arg ) const
{
	return m_object >= arg.m_object;
}


// -------------------------------------------------------------------------------------------
// template for type-safe usage of reference counted pointers
// -------------------------------------------------------------------------------------------
template<class type> 
class t_counted_ptr : public t_counted_ptr_base
{
public:
	typedef type element_type;

	t_counted_ptr();
	t_counted_ptr( type* object );

	template <class other_type>
	  t_counted_ptr( t_counted_ptr<other_type> const& pointer )
		: t_counted_ptr_base( implicit_cast<type*>( pointer.get() ) )
	{
	}

	t_counted_ptr( t_counted_ptr const& pointer );

	type*       get() const;

	t_counted_ptr& operator=( type* arg );

	template < class t_other_type >
	t_counted_ptr& operator=( t_counted_ptr< t_other_type > const& arg )
	{
		return static_cast<t_counted_ptr<type>&>(t_counted_ptr_base::operator=( 
												 implicit_cast<element_type *>( arg.get() )));
	}

	t_counted_ptr& operator=( t_counted_ptr const& arg );
	 		       operator type* () const;
	type*          operator->() const;
	type&          operator*() const;
};

// -------------------------------------------------------------------------------------------
// inlines for counted pointer template
// -------------------------------------------------------------------------------------------

template <class type>
inline t_counted_ptr<type>::t_counted_ptr() 
	: t_counted_ptr_base() 
{
}

template <class type>
inline t_counted_ptr<type>::t_counted_ptr( type* object ) 
	: t_counted_ptr_base( object ) 
{
}

template <class type>
inline t_counted_ptr<type>::t_counted_ptr( const t_counted_ptr& pointer ) 
                          : t_counted_ptr_base( pointer ) 
{
}

template <class type>
inline t_counted_ptr<type>& t_counted_ptr<type>::operator=( type* arg )
{
	return static_cast<t_counted_ptr<type>&>(t_counted_ptr_base::operator=(arg)); 
}

template <class type>
inline type* t_counted_ptr<type>::get() const
{ 
	return const_cast<type*>( static_cast< type const * >(m_object) ); 
}

template <class type>
inline t_counted_ptr<type>::operator type* () const
{
	return const_cast<type*>( static_cast< type const * >(m_object) ); 
}

template <class type>
inline type* t_counted_ptr<type>::operator->() const
{
	return const_cast<type*>( static_cast< type const * >(m_object) ); 
}

template <class type>
inline type& t_counted_ptr<type>::operator*() const
{ 
	return const_cast<type&>( static_cast< type const & >( *m_object) ); 
}

template <class type>
inline t_counted_ptr<type>& t_counted_ptr<type>::operator=( const t_counted_ptr<type>& arg )
{ 
	return static_cast<t_counted_ptr<type>&>(t_counted_ptr_base::operator=(arg)); 
}

// -------------------------------------------------------------------------------------------
// template for type-safe usage of reference counted pointers
// For pointers to const objects
// -------------------------------------------------------------------------------------------
template<class t_type> 
class t_counted_const_ptr : public t_counted_ptr_base
{
public:
	typedef t_type const t_element_type;

	t_counted_const_ptr();
	t_counted_const_ptr( t_element_type * object );

	template <class other_type>
	explicit t_counted_const_ptr( t_counted_const_ptr<other_type> const& pointer )
		: t_counted_ptr_base( implicit_cast<t_element_type*>( pointer.get() ) )
	{
	}

	t_counted_const_ptr( t_counted_const_ptr const& pointer );
	t_counted_const_ptr( t_counted_ptr<t_type> const& pointer );

	t_element_type* get() const;

	t_counted_const_ptr& operator=( t_element_type* arg );
	
	template < class t_other_type >
	  t_counted_const_ptr& operator=( t_counted_const_ptr< t_other_type > const& arg )
	{
		return static_cast<t_counted_const_ptr<t_type>&>(t_counted_ptr_base::operator=(implicit_cast<t_element_type *>( arg.get() ) ) ); 
	}

	template < class t_other_type >
	  t_counted_const_ptr& operator=( t_counted_ptr< t_other_type > const& arg )
	{
		return static_cast<t_counted_const_ptr<t_type>&>(t_counted_ptr_base::operator=(implicit_cast<t_element_type *>( arg.get() ) ) ); 
	}

	t_counted_const_ptr& operator=( t_counted_const_ptr const& arg );
	t_counted_const_ptr& operator=( t_counted_ptr<t_type> const& arg );
	 		             operator t_element_type* () const;
	t_element_type*      operator->() const;
	t_element_type&      operator*() const;
};

// -------------------------------------------------------------------------------------------
// inlines for counted pointer template
// -------------------------------------------------------------------------------------------

template <class t_type>
inline t_counted_const_ptr<t_type>::t_counted_const_ptr() : t_counted_ptr_base() 
{
}

template <class t_type>
inline t_counted_const_ptr<t_type>::t_counted_const_ptr( t_element_type* object ) 
	: t_counted_ptr_base( object ) 
{
}

template <class t_type>
inline t_counted_const_ptr<t_type>::t_counted_const_ptr( t_counted_const_ptr const& pointer ) 
                          : t_counted_ptr_base( pointer ) 
{
}

template <class t_type>
inline t_counted_const_ptr<t_type>::t_counted_const_ptr( t_counted_ptr<t_type> const& pointer )
								  : t_counted_ptr_base( pointer )
{
}

template <class t_type>
inline t_counted_const_ptr<t_type>& t_counted_const_ptr<t_type>::operator=( t_element_type * arg )
{
	return static_cast<t_counted_const_ptr<t_type>&>(t_counted_ptr_base::operator=(arg)); 
}

template <class t_type>
inline t_type const * t_counted_const_ptr<t_type>::get() const
{ 
	return static_cast<t_element_type*>(m_object); 
}

template <class t_type>
inline t_counted_const_ptr<t_type>::operator t_type const *() const
{
	return static_cast<t_element_type*>(m_object); 
}

template <class t_type>
inline t_type const * t_counted_const_ptr<t_type>::operator->() const
{
	return static_cast<t_element_type*>(m_object);
}

template <class t_type>
inline t_type const & t_counted_const_ptr<t_type>::operator*() const
{ 
	return static_cast<t_element_type&>(*m_object);
}

template < class t_type >
t_counted_const_ptr<t_type>& t_counted_const_ptr<t_type>::operator=( t_counted_const_ptr<t_type> const& arg )
{
	return static_cast<t_counted_const_ptr<t_type>&>( t_counted_ptr_base::operator=( arg )); 
}

template < class t_type >
t_counted_const_ptr<t_type>& t_counted_const_ptr<t_type>::operator=( t_counted_ptr<t_type> const& arg )
{
	return static_cast<t_counted_const_ptr<t_type>&>(t_counted_ptr_base::operator=( arg ) ); 
}


#endif